//@ts-nocheck
import CesiumGradualLightRingMaterialProperty from "./WallMaterial.js";
class YanMo {
  //适用于Cesium-1.99
  constructor(viewer) {
    this.viewer = viewer;
    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
    this.drawLayer = new Cesium.CustomDataSource("填挖方绘制图层");
    this.viewer.dataSources.add(this.drawLayer);
    this.polygon;
    this.polygonArr = [];
    this.tip;
    this.minHeight;
    this.maxHeight;
    this.isPlay = false;
    this.alpha = 0.8;
    this.animationId;
    this.waterHeight = 3000;
    this.timer = null;
    this.river;
    this.curWaterHeight = 0;
    this.speed = 1;
  }

  start(speed) {
    this.speed = speed;
    return new Promise((resolve, reject) => {
      let tempPolygon;
      let tempLine;
      this.handler.setInputAction((e) => {
        let pos = this.pxTo3C(e.position);
        if (!Cesium.defined(pos)) {
          return;
        }
        let point = this.createPoint(pos);
        this.drawLayer.entities.add(point);
        this.polygonArr.push(pos);
        if (this.polygonArr.length == 1) {
          this.handler.setInputAction((evt) => {
            let movePos = this.pxTo3C(evt.endPosition);
            if (!Cesium.defined(movePos)) {
              return;
            }
            if (Cesium.defined(tempLine)) {
              this.drawLayer.entities.remove(tempLine);
            }
            tempLine = new Cesium.Entity({
              polyline: {
                positions: new Cesium.CallbackProperty(() => {
                  return this.polygonArr.concat(movePos);
                }, false),
                material: Cesium.Color.YELLOW,
                width: 3,
              },
            });
            this.drawLayer.entities.add(tempLine);
          }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
        }
        if (this.polygonArr.length > 2) {
          this.handler.removeInputAction(
            Cesium.ScreenSpaceEventType.MOUSE_MOVE
          );
          this.drawLayer.entities.remove(tempLine);
          this.handler.setInputAction((e) => {
            let pos = this.pxTo3C(e.endPosition);
            if (!Cesium.defined(pos)) return;
            if (Cesium.defined(tempPolygon)) {
              this.drawLayer.entities.remove(tempPolygon);
            }
            tempPolygon = new Cesium.Entity({
              polygon: {
                hierarchy: new Cesium.CallbackProperty(() => {
                  return new Cesium.PolygonHierarchy(
                    this.polygonArr.concat(pos)
                  );
                }, false),
                material: Cesium.Color.AQUA.withAlpha(0.3),
                outline: true,
                outlineWidth: 2,
                outlineColor: Cesium.Color.YELLOW,
              },
            });
            this.drawLayer.entities.add(tempPolygon);
          }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
        }
        this.handler.setInputAction(async () => {
          this.removeEvent();
          if (Cesium.defined(tempLine)) {
            this.drawLayer.entities.remove(tempLine);
          }
          if (Cesium.defined(tempPolygon)) {
            this.drawLayer.entities.remove(tempPolygon);
          }
          this.drawLayer.entities.removeAll();
          this.polygon = new Cesium.Entity({
            polygon: {
              hierarchy: this.polygonArr,
              material: Cesium.Color.AQUA.withAlpha(0.3),
              outline: true,
              outlineWidth: 2,
              outlineColor: Cesium.Color.YELLOW,
            },
          });
          //   this.drawLayer.entities.add(this.polygon);
          // let res = await this.getHeight(this.polygonArr)
          // this.minHeight = res.minHeight
          // this.maxHeight = res.maxHeight
          // console.log(res,'获取区域高度极值');

          let river = await this.startUp(this.polygonArr);
          resolve({ positions: this.polygonArr, river: river });
        }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
      }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    });
  }

  async getHeight(positions) {
    return new Promise(async (resolve, reject) => {
      let rect = Cesium.Rectangle.fromCartesianArray(positions);
      console.log(rect);

      let leftTop = Cesium.Rectangle.northwest(rect, new Cesium.Cartographic());
      let rightBottom = Cesium.Rectangle.southeast(
        rect,
        new Cesium.Cartographic()
      );

      function radiansToDegrees(carto) {
        carto.longitude = Cesium.Math.toDegrees(carto.longitude);
        carto.latitude = Cesium.Math.toDegrees(carto.latitude);
      }

      function canDistance(cart1, cart2) {
        let point1 = Cesium.Cartesian3.fromDegrees(
          cart1.longitude,
          cart1.latitude
        );
        let point2 = Cesium.Cartesian3.fromDegrees(
          cart2.longitude,
          cart2.latitude
        );
        let distance = Cesium.Cartesian3.distance(point1, point2);
        return distance;
      }

      let distance = Math.round(canDistance(leftTop, rightBottom) / 1.414);
      radiansToDegrees(leftTop);
      radiansToDegrees(rightBottom);
      let minLongitude = Math.min(leftTop.longitude, rightBottom.longitude);
      let maxLongitude = Math.max(leftTop.longitude, rightBottom.longitude);
      let minLatitude = Math.min(leftTop.latitude, rightBottom.latitude);
      let maxLatitude = Math.max(leftTop.latitude, rightBottom.latitude);
      let lngLatArr = [minLongitude, minLatitude, maxLongitude, maxLatitude];
      let cellSide = distance / 800;

      let grid = turf.pointGrid(lngLatArr, cellSide);
      let coords = turf.coordAll(grid);

      let flatArr = [];
      coords.forEach((e) => {
        let pos = Cesium.Cartesian3.fromDegrees(e[0], e[1]);
        flatArr.push(pos);
      });
      let findRes = await this.viewer.scene.clampToHeightMostDetailed(flatArr);
      findRes = findRes.filter((e) => {
        return Cesium.defined(e);
      });
      let minPoint;
      let maxPoint;
      let minHeight;
      let maxHeight;
      findRes.forEach((item) => {
        let height = Cesium.Cartographic.fromCartesian(item).height;

        if (!minHeight) {
          minHeight = height;
        }
        if (!maxHeight) {
          maxHeight = height;
        }
        minHeight = Math.min(minHeight, height);
        maxHeight = Math.max(maxHeight, height);
        if (minHeight == height) {
          minPoint = item;
        }
        if (maxHeight == height) {
          maxPoint = item;
        }
      });
      //  //#region 开启后可以展示点位
      findRes.forEach((e) => {
        let color = Cesium.Color.YELLOW;
        let size = 3;
        if (e == minPoint) {
          color = Cesium.Color.GREEN;
          size = 10;
        }
        if (e == maxPoint) {
          color = Cesium.Color.RED;
          size = 10;
        }
        let point = new Cesium.Entity({
          position: e,
          point: {
            pixelSize: size,
            color: color,
          },
        });
        this.drawLayer.entities.add(point);
      });
      // //#endregion 开启后可以展示点位
      let obj = {
        minHeight,
        minPoint,
        maxHeight,
        maxPoint,
      };
      resolve(obj);
    });
  }

  async startUp(positions) {
    return new Promise((resolve, reject) => {
      let that = this;
      positions.forEach((e) => {
        let carto = Cesium.Cartographic.fromCartesian(e);
        let height = this.viewer.scene.globe.getHeight(carto);
        if (!that.minHeight) {
          that.minHeight = height;
        } else {
          that.minHeight = Math.min(that.minHeight, height);
        }
        if (!that.maxHeight) {
          that.maxHeight = height;
        } else {
          that.maxHeight = Math.max(that.maxHeight, height);
        }
      });

      let rectangle = Cesium.Rectangle.fromCartesianArray(that.polygonArr);
      let arr = [];
      let pos1 = Cesium.Rectangle.northwest(
        rectangle,
        new Cesium.Cartographic()
      );
      arr.push(Cesium.Cartesian3.fromRadians(pos1.longitude, pos1.latitude));
      let pos2 = Cesium.Rectangle.northeast(
        rectangle,
        new Cesium.Cartographic()
      );
      arr.push(Cesium.Cartesian3.fromRadians(pos2.longitude, pos2.latitude));
      let pos3 = Cesium.Rectangle.southeast(
        rectangle,
        new Cesium.Cartographic()
      );
      arr.push(Cesium.Cartesian3.fromRadians(pos3.longitude, pos3.latitude));
      let pos4 = Cesium.Rectangle.southwest(
        rectangle,
        new Cesium.Cartographic()
      );
      arr.push(Cesium.Cartesian3.fromRadians(pos4.longitude, pos4.latitude));
      positions = arr;

      let wallPositions = positions.concat(positions[0]);
      this.drawLayer.entities.add(
        new Cesium.Entity({
          wall: {
            positions: wallPositions,
            minimumHeights: Array(wallPositions.length).fill(0),
            maximumHeights: Array(wallPositions.length).fill(
              this.maxHeight + (that.maxHeight - that.minHeight)
            ),
            fill: true,
            material: new CesiumGradualLightRingMaterialProperty({
              color: Cesium.Color.YELLOW.withAlpha(0.5),
              rate: 1,
              repeatNum: 5,
            }),
          },
        })
      );

      let riverMaterial = new Cesium.MaterialAppearance({
        material: new Cesium.Material({
          fabric: {
            uniforms: {
              iTime: 0,
              alpha: 0.5,
            },
            source: `
                  const int NUM_STEPS = 8;
                  const float PI = 3.141592;
                  const float EPSILON = 1e-3;
                  #define EPSILON_NRM (0.1 / 200.0)
  
                  const int ITER_GEOMETRY = 3;
                  const int ITER_FRAGMENT = 5;
                  const float SEA_HEIGHT = 0.6;
                  const float SEA_CHOPPY = 4.0;
                  const float SEA_SPEED = 1.8;
                  const float SEA_FREQ = 0.16;
                  const vec3 SEA_BASE = vec3(0.1,0.19,0.22);
                  const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6);
  
                  const mat2 octave_m = mat2(1.6,1.2,-1.2,1.6);
  
                  mat3 fromEuler(vec3 ang) {
                      vec2 a1 = vec2(sin(ang.x),cos(ang.x));
                      vec2 a2 = vec2(sin(ang.y),cos(ang.y));
                      vec2 a3 = vec2(sin(ang.z),cos(ang.z));
                      mat3 m;
                      m[0] = vec3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x);
                      m[1] = vec3(-a2.y*a1.x,a1.y*a2.y,a2.x);
                      m[2] = vec3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y);
                      return m;
                  }
  
                  float hash(vec2 p) {
                      float h = dot(p,vec2(127.1,311.7));
                      return fract(sin(h)*43758.5453123);
                  }
  
                  float noise(vec2 p) {
                      vec2 i = floor(p);
                      vec2 f = fract(p);
                      vec2 u = f*f*(3.0-2.0*f);
                      return -1.0+2.0*mix(mix(hash(i + vec2(0.0,0.0)), hash(i + vec2(1.0,0.0)), u.x), mix(hash(i + vec2(0.0,1.0)), hash(i + vec2(1.0,1.0)), u.x), u.y);
                  }
  
                  float diffuse(vec3 n,vec3 l,float p) {
                      return pow(dot(n,l) * 0.4 + 0.6,p);
                  }
  
                  float specular(vec3 n,vec3 l,vec3 e,float s) {
                      float nrm = (s + 8.0) / (PI * 8.0);
                      return pow(max(dot(reflect(e,n),l),0.0),s) * nrm;
                  }
  
                  vec3 getSkyColor(vec3 e) {
                      e.y = max(e.y,0.0);
                      return vec3(pow(1.0-e.y,2.0), 1.0-e.y, 0.6+(1.0-e.y)*0.4);
                  }
  
                  float sea_octave(vec2 uv, float choppy) {
                      uv += noise(uv);
                      vec2 wv = 1.0-abs(sin(uv));
                      vec2 swv = abs(cos(uv));
                      wv = mix(wv,swv,wv);
                      return pow(1.0-pow(wv.x * wv.y,0.65),choppy);
                  }
  
                  float map(vec3 p) {
                      float freq = SEA_FREQ;
                      float amp = SEA_HEIGHT;
                      float choppy = SEA_CHOPPY;
                      vec2 uv = p.xz; uv.x *= 0.75;
                      float d, h = 0.0;
                      float SEA_TIME = 1.0 + iTime * SEA_SPEED;
                      for(int i = 0; i < ITER_GEOMETRY; i++) {
                          d = sea_octave((uv+SEA_TIME)*freq,choppy);
                          d += sea_octave((uv-SEA_TIME)*freq,choppy);
                          h += d * amp;
                          uv *= octave_m; freq *= 1.9; amp *= 0.22;
                          choppy = mix(choppy,1.0,0.2);
                      }
                      return p.y - h;
                  }
  
                  float map_detailed(vec3 p) {
                      float freq = SEA_FREQ;
                      float amp = SEA_HEIGHT;
                      float choppy = SEA_CHOPPY;
                      vec2 uv = p.xz; uv.x *= 0.75;
                      float SEA_TIME = 1.0 + iTime * SEA_SPEED;
                      float d, h = 0.0;
                      for(int i = 0; i < ITER_FRAGMENT; i++) {
                          d = sea_octave((uv+SEA_TIME)*freq,choppy);
                          d += sea_octave((uv-SEA_TIME)*freq,choppy);
                          h += d * amp;
                          uv *= octave_m; freq *= 1.9; amp *= 0.22;
                          choppy = mix(choppy,1.0,0.2);
                      }
                      return p.y - h;
                  }
  
                  vec3 getSeaColor(vec3 p, vec3 n, vec3 l, vec3 eye, vec3 dist) {
                      float fresnel = clamp(1.0 - dot(n,-eye), 0.0, 1.0);
                      fresnel = pow(fresnel,3.0) * 0.65;
                      vec3 reflected = getSkyColor(reflect(eye,n));
                      vec3 refracted = SEA_BASE + diffuse(n,l,80.0) * SEA_WATER_COLOR * 0.12;
                      vec3 color = mix(refracted,reflected,fresnel);
                      float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0);
                      color += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten;
                      color += vec3(specular(n,l,eye,60.0));
                      return color;
                  }
  
                  vec3 getNormal(vec3 p, float eps) {
                      vec3 n;
                      n.y = map_detailed(p);
                      n.x = map_detailed(vec3(p.x+eps,p.y,p.z)) - n.y;
                      n.z = map_detailed(vec3(p.x,p.y,p.z+eps)) - n.y;
                      n.y = eps;
                      return normalize(n);
                  }
  
                  float heightMapTracing(vec3 ori, vec3 dir, out vec3 p) {
                      float tm = 0.0;
                      float tx = 1000.0;
                      float hx = map(ori + dir * tx);
                      if(hx > 0.0) return tx;
                      float hm = map(ori + dir * tm);
                      float tmid = 0.0;
                      for(int i = 0; i < NUM_STEPS; i++) {
                          tmid = mix(tm,tx, hm/(hm-hx));
                          p = ori + dir * tmid;
                          float hmid = map(p);
                          if(hmid < 0.0) {
                              tx = tmid;
                              hx = hmid;
                          } else {
                              tm = tmid;
                              hm = hmid;
                          }
                      }
                      return tmid;
                  }
  
                  vec4 czm_getMaterial(vec2 vUv) {
                      vec2 uv = vUv;
                      uv = vUv * 2.0 - 1.0;
                      float time = iTime * 0.3 + 0.0 * 0.01;
                      vec3 ang = vec3(0, 1.2, 0.0);
                      vec3 ori = vec3(0.0,3.5,0);
                      vec3 dir = normalize(vec3(uv.xy,-2.0));
                      dir.z += length(uv) * 0.15;
                      dir = normalize(dir) * fromEuler(ang);
                      vec3 p;
                      heightMapTracing(ori,dir,p);
                      vec3 dist = p - ori;
                      vec3 n = getNormal(p, dot(dist,dist) * EPSILON_NRM);
                      vec3 light = normalize(vec3(0.0,1.0,0.8));
                      vec3 color = mix(
                          getSkyColor(dir),
                          getSeaColor(p,n,light,dir,dist),
                          pow(smoothstep(0.0,-0.05,dir.y),0.3));
                      return vec4(pow(color.rgb, vec3(0.75)), alpha);
                  }
              `,
          },
        }),
        translucent: true,

        fragmentShaderSource: `
          varying vec2 v_st;
          varying vec3 v_positionEC;
          varying vec3 v_normalEC;
  
          void main() {
              vec3 positionToEyeEC = -v_positionEC;
              vec3 normalEC = normalize(v_normalEC);
              czm_materialInput materialInput;
              materialInput.normalEC = normalEC;
              materialInput.positionToEyeEC = positionToEyeEC;
              materialInput.st = v_st;
              vec4 color = czm_getMaterial(v_st);
              out_FragColor = color;
          }
      `,
      });

      // 使用 riverMaterial 来创建 Primitive 或者其他适当的 Cesium 实体

      function useDrawRiver(boundary, extrudedHeight = 0) {
        let riverHeight = extrudedHeight;
        const polygon = useCreatePolygonGeometry(boundary, riverHeight);

        let river = new Cesium.Primitive({
          geometryInstances: new Cesium.GeometryInstance({
            geometry: polygon,
          }),
          appearance: riverMaterial,
          show: true,
          asynchronous: false,
          releaseGeometryInstances: false,
        });

        // 自定义材质
        let scene = that.viewer.scene;
        scene.primitives.add(river);

        Object.defineProperty(river, "extrudedHeight", {
          get() {
            return riverHeight;
          },
          set(newVal) {
            if (typeof newVal !== "number") {
              return;
            }
            riverHeight = newVal;
            river._state = 3;
            river._appearance = undefined;
            river.geometryInstances.geometry = useCreatePolygonGeometry(
              boundary,
              riverHeight
            );
          },
        });
        function renderLoop(timestamp) {
          riverMaterial.material.uniforms.iTime = timestamp / 1800;
          riverMaterial.material.uniforms.alpha = that.alpha; // 动态设置透明度为50%
          that.animationId = requestAnimationFrame(renderLoop);
        }
        renderLoop();

        return river;
      }

      function useCreatePolygonGeometry(boundary, extrudedHeight) {
        const polygon = new Cesium.PolygonGeometry({
          polygonHierarchy: new Cesium.PolygonHierarchy(
            Cesium.Cartesian3.fromDegreesArray(boundary)
          ),
          extrudedHeight: extrudedHeight,
          vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
        });
        return polygon;
      }

      let maxHeight = 30000;

      let boundary = [];
      positions.forEach((e) => {
        let cart = Cesium.Cartographic.fromCartesian(e);
        boundary.push(Cesium.Math.toDegrees(cart.longitude));
        boundary.push(Cesium.Math.toDegrees(cart.latitude));
      });
      let riverHeight = 0;
      that.river = useDrawRiver(boundary);

      that.timer = setInterval(() => {
        riverHeight += that.speed;
        that.river.extrudedHeight = riverHeight;
        this.curWaterHeight = riverHeight;

        if (riverHeight >= that.maxHeight) {
          riverHeight = that.maxHeight;
          // 清除定时器
          clearInterval(that.timer);
        }
      }, 10);
      resolve(that.river);
    });
  }

  stopAnimation(isStop) {
    if (isStop) {
      if (this.timer) {
        clearInterval(this.timer);
      }
    } else {
      let that = this;
      let riverHeight = that.curWaterHeight;
      that.timer = setInterval(() => {
        riverHeight += that.speed;
        that.river.extrudedHeight = riverHeight;
        that.curWaterHeight = riverHeight;
        if (riverHeight >= that.maxHeight) {
          riverHeight = that.maxHeight;

          // 清除定时器
          clearInterval(that.timer);
        }
      }, 10);
    }
  }

  clearYanMo() {
    this.stopAnimation(true);
    if (this.animationId) {
      cancelAnimationFrame(this.animationId);
      this.animationId = null;
    }
    this.drawLayer.entities.removeAll();
    if (this.river) {
      this.viewer.scene.primitives.remove(this.river);
      this.river = null;
      console.log("河水移除");
    }
    this.curWaterHeight = 0;
    cancelAnimationFrame(this.animationId);
    this.animationId = null;
    this.polygonArr = [];
    this.isPlay = false;
    this.minHeight = null;
    this.maxHeight = null;
    this.curWaterHeight = 0;
    this.alpha = 0.8;
    this.speed = 1;
  }

  removeEvent() {
    this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
    this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    this.handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  }

  pxTo3C(cartesian2) {
    let ray = this.viewer.camera.getPickRay(cartesian2);
    let cartesian3 = this.viewer.scene.globe.pick(ray, this.viewer.scene);
    return cartesian3;
  }

  createPoint(position) {
    let point = new Cesium.Entity({
      position: position,
      point: {
        pixelSize: 5,
        color: Cesium.Color.fromCssColorString("#3388ff"),
        // outline: true,
        // outlineWidth: 2,
        // outlineColor: Cesium.Color.WHITE
      },
    });
    return point;
  }
}

export default YanMo;
